iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0

昨天,我用 Console.ReadLine() 做了一個很簡單的實驗。

我第一次真正理解到,程式「還活著」,不代表 CPU 必須一直空轉。

程式也可以停在某個地方,等待外部事件發生。

在 Console 程式裡,等待的是:

鍵盤輸入

但 Web Server 當然不可能靠Console.ReadLine(),來等 Browser。

所以新的問題就出現了:

Web Server 到底在等什麼?

Browser 的 Request 又是怎麼真正進入我的 C# 程式?

今天,我第一次讓自己的 Mini Web Framework 專案真正碰到網路。


如果 Web Server 要等待 Client,那它到底是在等待什麼?

以前聽到:

Server 正在監聽 Port 5000。

我其實只是把這句話背起來。

像 ASP.NET Core 常看到:

localhost:5000

我知道 5000 是 Port。

也知道 Browser 可以連進去。

但如果真的要我回答:

「你的 C# 程式到底怎麼知道有人連到 Port 5000?」

我其實回答不出來。

所以今天我不想直接使用 ASP.NET Core,我想先從最基本的 TCP Server 開始。

我先建立:

var listener = new TcpListener(IPAddress.Loopback, 5000);

接著啟動:

listener.Start();

並印出:

Console.WriteLine("Server 已啟動,正在等待連線...");

接著最重要的是:

TcpClient client = listener.AcceptTcpClient();

執行後,我看到:

Server 已啟動,正在等待連線...

然後程式就停在那裡。

一開始我差點以為程式卡住了。

但仔細想想,這其實正是我想要的結果。

因為:

AcceptTcpClient();

現在正在等待 Client 建立 TCP 連線。

這讓我第一次把昨天的概念接到網路:

Day9

Console.ReadLine()
↓
等待鍵盤輸入

今天變成:

Day10

AcceptTcpClient()
↓
等待網路 Client

https://ithelp.ithome.com.tw/upload/images/20260811/20183395nYE5pSLrxx.png

IP 和 Port 到底在做什麼?

今天的 Server 建立在:

IPAddress.Loopback

以及:

Port 5000

Loopback 對應的就是:

127.0.0.1

也就是:

這台電腦自己,所以今天的 Client 和 Server 都在同一台電腦。

可以簡化成:

Client
│
│ 127.0.0.1:5000
▼
Server

而 Port 可以先理解成一個「入口編號」。

同一台電腦可能同時執行很多網路程式:

127.0.0.1:5000
127.0.0.1:5001
127.0.0.1:8080

Port 讓作業系統知道:

這個網路資料到底要交給哪一個程式。

所以:

IP
↓
找到哪一台電腦

Port
↓
找到這台電腦上的哪個服務


接著,我另外建立 Client 去連:

127.0.0.1:5000

當 Client 成功連線後,原本 Server 的畫面立刻出現:

收到一個連線!

這一刻其實很重要。

因為我第一次親眼看到:

Client
│
│ 建立 TCP 連線
▼
TcpListener
│
▼
AcceptTcpClient()
│
▼
收到一個連線

也就是說,Server 並不是一直「猜」有沒有人來,它可以停在等待連線的狀態,直到真的有 Client 連進來。


接著,我透過:

NetworkStream stream = client.GetStream();

取得網路資料流。

再使用:

int length = stream.Read(buffer, 0, buffer.Length);

讀取 Client 傳來的資料。

這裡又出現了一個有趣的現象。

Server 已經顯示:

收到一個連線!

但程式又停住了。

一開始我以為是不是出錯。

後來才發現:

建立連線和收到資料,其實是兩個不同階段。

流程其實是:

等待 Client
↓
Client 建立連線
↓
AcceptTcpClient() 完成
↓
等待資料
↓
stream.Read()
↓
收到資料

所以:

「有人連進來」

不代表:

「對方已經把資料送過來」

這是今天另一個很重要的發現。

https://ithelp.ithome.com.tw/upload/images/20260811/20183395ZMeEwIA7HP.png

https://ithelp.ithome.com.tw/upload/images/20260811/20183395Rhn6b0tK8e.png

https://ithelp.ithome.com.tw/upload/images/20260811/20183395wUGvoYy8s4.png

https://ithelp.ithome.com.tw/upload/images/20260811/20183395glviJLhVU2.png

Socket 到底是什麼?

做到這裡,我才開始理解 Socket 的角色。

以前看到 Socket,我總覺得它是一個很抽象的網路名詞。

但今天可以先把它理解成:

程式和網路世界溝通的一個端點。

Client 需要一個網路端點。

Server 也需要一個網路端點。

雙方透過 IP、Port 和 TCP 建立連線後,就可以開始交換資料。

可以簡化成:

Client Process
│
Socket
│
│ TCP
│
Socket
│
Server Process

而今天 C# 提供的:

TcpListener

和:

TcpClient

其實都是幫我們包裝底層 Socket 操作的類別。

所以今天我雖然沒有直接操作最底層的 Socket 類別,但已經第一次真正透過 TCP 建立 Client 與 Server 的連線。


官方怎麼說?

Microsoft 對 TcpListener 的描述,可以簡化理解為:

它提供監聽 TCP 網路 Client 連線的能力。

而:

AcceptTcpClient()

會等待傳入的 TCP 連線,成功後取得一個代表該連線的 TcpClient。

接著:

GetStream()

讓 Server 可以透過 NetworkStream 接收或傳送資料。

所以今天自己做出的流程:

Listen
↓
Accept
↓
GetStream
↓
Read

並不是我自己亂猜出來的流程,而是 TCP Server 實際工作的基本步驟。


昨天的我:

Web Server 可以一直活著,因為它會等待 Request。

今天的我:

Server 並不是直接等待「HTTP Request」這個抽象概念,它更底層會先等待網路連線,取得連線後,再從網路資料流中讀取 Client 傳來的 bytes。

也就是:

Client
↓
建立 TCP 連線
↓
Server Accept
↓
NetworkStream
↓
讀取 bytes
↓
才開始理解資料內容

我以前都是直接從:

HTTP Request 開始學。

今天才第一次知道,在 HTTP 出現以前,下面其實還有 TCP 和 Socket。


今天是 Mini Web Framework 第一次真正具備「網路能力」的一天。

前面的專案本質上都還只是:

Console Application

但今天它已經可以:

啟動
↓
監聽 Port 5000
↓
等待 Client
↓
接受 TCP 連線
↓
讀取 Client 傳來的資料
↓
繼續等待

雖然現在還沒有 Routing、Controller、Middleware,甚至還不懂 HTTP。

但它已經跨過非常重要的一步,它不再只是自己執行自己的程式,而是開始能接收外面的世界傳進來的資料。

這也是從普通程式走向 Web Server 的真正起點。


上一篇
為什麼一般程式執行完就結束,但 Web Server 可以一直等 Request?
下一篇
Browser 傳來的 Request 到底長什麼樣?
系列文
程式設計沒有告訴你的事:30 天破解每一個 Why 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言